home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / System / CTreeViewer Archive / CTreeViewer.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-03-07  |  15.9 KB  |  652 lines  |  [TEXT/KAHL]

  1. /******************************************************************************/
  2. /*                                                                            */
  3. /*                                CTreeViewer                                 */
  4. /*                                                                            */
  5. /* This program graphs the visual hierarchy and the chain of command.  To use */
  6. /* it, include the class into your project, and call ITreeViewer in your      */
  7. /* application's initialization method.  Then call one of the three viewing   */
  8. /* methods described below.                                                   */
  9. /*                                                                            */
  10. /* ITreeViewer (short WINDid, Boolean floating, CDesktop *anEnclosure,        */
  11. /*                                            CDirectorOwner *aSupervisor)    */
  12. /*      If WINDid != 0, the window is read from a WIND resource.  The window  */
  13. /*      should not have a close box.                                          */
  14. /*                                                                            */
  15. /* ShowSubviews (CView *theView)                                              */
  16. /*      This shows the subviews enclosed by theView, in the form of a tree.   */
  17. /*                                                                            */
  18. /* ShowEnclosures (CView *theView)                                            */
  19. /*      This shows all the views that enclose theView.                        */
  20. /*                                                                            */
  21. /* ShowSupervisors (CBureaucrat *theBureaucrat)                               */
  22. /*      This shows the chain of command starting from theBureaucrat and       */
  23. /*      working upward.                                                       */
  24. /*                                                                            */
  25. /******************************************************************************/
  26.  
  27. #include "CTreeViewer.h"
  28. #include <CBartender.h>
  29. #include <CBureaucrat.h>
  30. #include <CClipboard.h>
  31. #include <CDirectorOwner.h>
  32. #include <CDesktop.h>
  33. #include <CList.h>
  34. #include <CPicture.h>
  35. #include <CScrollPane.h>
  36. #include <CView.h>
  37. #include <CWindow.h>
  38. #include <Commands.h>
  39. #include <TBUtilities.h>
  40.  
  41. extern CDesktop        *gDesktop;
  42. extern CBartender    *gBartender;
  43. extern CClipboard    *gClipboard;
  44.  
  45. //
  46. //                              ITreeViewer
  47. //
  48. // Initialize the instance of this class.  If WINDid == 0, it creates its own,
  49. // otherwise it reads the window from a resource with ID WINDid.  The other
  50. // inputs are the same as for CWIndow::IWindow.
  51. //
  52.  
  53. void CTreeViewer :: ITreeViewer (short WINDid,
  54.                                 Boolean floating,
  55.                                 CDesktop *anEnclosure,
  56.                                 CDirectorOwner *aSupervisor)
  57. {
  58.     PicHandle    aPicture;
  59.     Rect        theRect;
  60.     long        itsID;
  61. //
  62. // Initialize this director by calling its superclass' initialization
  63. //
  64.     CDirector::IDirector (aSupervisor);
  65. //
  66. // Create a window for this
  67. //
  68.     itsWindow = new CWindow;
  69.     if (WINDid != 0)
  70.     {
  71.         itsWindow->IWindow(WINDid, floating, anEnclosure, this);
  72.         itsID = WINDid;
  73.     }
  74.     else
  75.     {
  76.         Rect theRect = {100, 100, 200, 400};
  77.  
  78.         itsWindow->INewWindow (&theRect, true, zoomDocProc, floating, false, 
  79.                     anEnclosure, this);
  80.         itsWindow->SetTitle("\pTree Viewer");
  81.         itsID = 'Tree';
  82.     }
  83.     itsWindow->SetID (itsID);
  84. //
  85. // Create a new scrollpane and install it in this window
  86. //
  87.     itsScrollPane = new CScrollPane;
  88.     itsScrollPane->IScrollPane (itsWindow, this, 0, 0, 0, 0, sizELASTIC,
  89.                                 sizELASTIC, true, true, true);
  90.     itsScrollPane->FitToEnclFrame (true, true);
  91.     itsScrollPane->SetSteps (kSVCellDepth, kSVCellWidth);
  92.     itsScrollPane->SetID (itsID);
  93. //
  94. // Create a CPicture and install it in the scrollpane.  For the moment, it is
  95. // empty
  96. //
  97.     itsPicture = new CPicture;
  98.     itsPicture->IPicture (itsScrollPane, this, 0, 0, 0, 0, sizELASTIC,
  99.                             sizELASTIC);
  100.     itsPicture->FitToEnclosure (true, true);
  101.     itsScrollPane->InstallPanorama(itsPicture);
  102. }
  103.  
  104.  
  105.  
  106. //
  107. //                              ShowSubviews
  108. //
  109. // This method draws the subviews of the given view in tree form.
  110. //
  111.  
  112. void CTreeViewer :: ShowSubviews (CView *theView)
  113. {
  114.     short        width, depth, hSize, vSize;
  115.     PicHandle    thePicH;
  116.     Rect        theRect;
  117.     GrafPort    myPort;
  118.     GrafPtr        oldPort, myPortPtr = &myPort;
  119.  
  120. //
  121. // Take the old picture (if any) and get rid of it
  122. //
  123.     thePicH = itsPicture->GetMacPicture();
  124.     if (thePicH) KillPicture (thePicH);
  125.  
  126. //
  127. // Call GetSizeSV will get the size of the resulting picture in units of cells
  128. // (depth cells deep and width cells wide).  Each cell occupies kSVCellDepth
  129. // pixels horizontally and kSVCellWidth vertically.
  130. //
  131.     depth = 0;
  132.     GetSizeSV (theView, &width, &depth, 0);
  133.     hSize = kSVCellDepth * depth;
  134.     vSize = kSVCellWidth * width;
  135.  
  136. //
  137. // Here we create the picture that we will install in the panorama.  We start
  138. // by creating our own GrafPort which we will draw into.  Creating our own
  139. // GrafPort allows us to fiddle with the text font and size, as well as adjust
  140. // the clip region (necessary for the OpenPicture to work right)
  141. //
  142.     SetRect (&theRect, 0, 0, hSize, vSize);
  143.     GetPort (&oldPort);
  144.     OpenPort (myPortPtr);
  145.     SetPort (myPortPtr);
  146.     TextFont (1);
  147.     TextSize (9);
  148. //    ClipRect (&thePort->portRect);
  149.     ClipRect (&theRect);
  150.     thePicH = OpenPicture (&theRect);
  151.     DrawTreeSV (theView, 1, vSize/2, width, true);
  152.     ClosePicture ();
  153.     SetPort (oldPort);
  154.     ClosePort (myPortPtr);
  155. //
  156. // Now install this picture
  157. //
  158.     InstallPicture (thePicH, hSize, vSize);
  159. }
  160.  
  161.  
  162. //
  163. //                              GetSizeSV
  164. //
  165. // This method finds the width and depth of the subview tree starting at
  166. // theView. It calls itself recursively to search the tree.
  167. //
  168. // Input:  theView   - the view at the top of this branch of the tree
  169. //         thisDepth - the depth of this node in the tree
  170. //         *depth_p  - the maximum depth found so far
  171. // Output: width     - the width of this branch of the tree
  172. //         *depth_p  - the maximum depth found so far
  173.  
  174. void CTreeViewer :: GetSizeSV (CView *theView, short *width, short *depth_p,
  175.                     short thisDepth)
  176. {
  177.     CList            *theList;
  178.     CView            *itsSubview;
  179.     short            itsWidth, sum;
  180.     long            i, n;
  181.  
  182.     thisDepth += 1;
  183.     if (thisDepth > *depth_p) *depth_p = thisDepth;
  184.     if (theView == gDesktop)
  185.     {
  186.         theList = gDesktop->itsWindows;
  187.     }
  188.     else
  189.     {
  190.         theList = theView->itsSubviews;
  191.     }
  192.     if (theList)
  193.     {
  194.         n = theList->GetNumItems();
  195.         sum = 0;
  196.         for (i=1; i<=n; i++)
  197.         {
  198.             itsSubview = (CView *)theList->NthItem(i);
  199.             GetSizeSV (itsSubview, &itsWidth, depth_p, thisDepth);
  200.             sum += itsWidth;
  201.         }
  202.     }
  203.     else
  204.     {
  205.         sum = 1;
  206.     }
  207.     *width = sum;
  208. }
  209.  
  210.  
  211. //
  212. //                              DrawTreeSV
  213. //
  214. // This method does the actual drawing of the tree.
  215. //
  216. // Input:  theView - the view at the top of this branch of the tree
  217. //         h, v    - the point at which to start drawing (window coordinates)
  218. //         myWidth - the width of this branch
  219. //         first   - controls whether to draw a stem
  220.  
  221. void CTreeViewer :: DrawTreeSV (CView *theView, short h, short v,
  222.             short myWidth, Boolean first)
  223. {
  224.     Str63    name, ID;
  225.     Rect    theRect;
  226.     short    textw, temp, itsWidth, sum, itsv;
  227.     long    i, n;
  228.     CList    *theList;
  229.     CView    *itsSubview;
  230.  
  231. //
  232. // Draw the stem
  233. //
  234.     if (!first)
  235.     {
  236.         MoveTo (h, v);
  237.         h += 4;
  238.         LineTo (h, v);
  239.     }
  240.  
  241.     SetRect (&theRect, h, v-kSVBoxHeight/2, h+kSVBoxWidth, v+kSVBoxHeight/2);
  242. //
  243. // Draw a gray box if it invisible, with a thick line if it is active, and make
  244. // it a rounded rectangle if the view doesn't want mouse clicks
  245. //
  246.     if (theView->ReallyVisible())
  247.         PenPat (black);
  248.     else
  249.         PenPat (gray);
  250.  
  251.     if (theView->IsActive())
  252.         PenSize (2,2);
  253.     else
  254.         PenSize (1,1);
  255.  
  256.     if (theView->GetWantsClicks())
  257.         FrameRect (&theRect);
  258.     else
  259.         FrameRoundRect (&theRect, 12, 12);
  260.     PenPat (black);
  261.     PenSize (1,1);
  262.  
  263. //
  264. // Get the name of the class, and the ID.  If the ID is a resource number,
  265. // convert it to a string; if it is a 4-character name, get it.
  266. //
  267.     theView->GetClassName(name);
  268.     i = theView->GetID();
  269.     if ((i < 0 && i >= -32768) || (i > 0 && i <= 32767))
  270.     {
  271.         NumToString (i, ID);
  272.         ConcatPStrings (name, "\p ");
  273.         ConcatPStrings (name, ID);
  274.     }
  275.     if (LongToStr(i, ID))
  276.     {
  277.         ConcatPStrings (name, ID);
  278.     }
  279.     textw = StringWidth (name);
  280.     MoveTo (h + kSVBoxWidth/2 - textw/2, v+3);
  281.     DrawString (name);
  282. //
  283. // Now get the list of views enclosed by this view
  284. //
  285.     if (theView == gDesktop)
  286.     {
  287.         theList = gDesktop->itsWindows;
  288.     }
  289.     else
  290.     {
  291.         theList = theView->itsSubviews;
  292.     }
  293.  
  294.     if (theList)
  295.     {
  296.         n = theList->GetNumItems();
  297. //
  298. // Draw the stem
  299. //
  300.         h += kSVBoxWidth;
  301.         MoveTo (h,v);
  302.         h += 4;
  303.         LineTo (h,v);
  304.         if (n == 1)
  305.         {
  306.             itsSubview = (CView *)theList->FirstItem();
  307.             DrawTreeSV (itsSubview, h, v, myWidth, false);
  308.         }
  309.         else
  310.         {
  311.             sum = 0;
  312.             for (i=1; i<=n; i++)
  313.             {
  314.                 itsSubview = (CView *)theList->NthItem(i);
  315.                 GetSizeSV (itsSubview, &itsWidth, &temp, 0);
  316.                 itsv = v + kSVCellWidth * sum + (kSVCellWidth/2) * (itsWidth - myWidth);
  317. //
  318. // Draw the branch
  319. //
  320.                 if (i == 1 || i == n)
  321.                 {
  322.                     MoveTo (h, v);
  323.                     LineTo (h, itsv);
  324.                 }
  325. //
  326. // Call this method recursively to draw the subviews of this view
  327. //
  328.                 DrawTreeSV (itsSubview, h, itsv, itsWidth, false);
  329.                 sum += itsWidth;
  330.             }
  331.         }
  332.     }
  333. }
  334.  
  335.  
  336.  
  337. //
  338. //                              ShowEnclosures
  339. //
  340. // This method draws the enclosures of this view.  This method is almost
  341. // identical to ShowSubviews: see that method for comments
  342. //
  343.  
  344. void CTreeViewer :: ShowEnclosures (CView *theView)
  345. {
  346.     short        height, hSize, vSize;
  347.     PicHandle    thePicH;
  348.     Rect        theRect;
  349.     GrafPort    myPort;
  350.     GrafPtr        oldPort, myPortPtr = &myPort;
  351.  
  352.     thePicH = itsPicture->GetMacPicture();
  353.     if (thePicH) KillPicture (thePicH);
  354.  
  355.     height = 0;
  356.     GetSizeE (theView, &height);
  357.     hSize = kECellWidth;
  358.     vSize = kECellHeight * height;
  359.  
  360.     SetRect (&theRect, 0, 0, hSize, vSize);
  361.     GetPort (&oldPort);
  362.     OpenPort (myPortPtr);
  363.     SetPort (myPortPtr);
  364.     TextFont (1);
  365.     TextSize (9);
  366.     ClipRect (&theRect);
  367.     thePicH = OpenPicture (&theRect);
  368.     DrawHierE (theView, hSize/2, vSize, true);
  369.     ClosePicture ();
  370.     SetPort (oldPort);
  371.     ClosePort (myPortPtr);
  372.  
  373.     InstallPicture (thePicH, hSize, vSize);
  374. }
  375.  
  376.  
  377. //
  378. //                              GetSizeE
  379. //
  380. // This method finds the number of enclosures (height) surrounding theView, up
  381. // to the gDesktop.
  382. //
  383.  
  384. void CTreeViewer :: GetSizeE (CView *theView, short *height_p)
  385. {
  386.     CView    *itsEnclosure;
  387.  
  388.     *height_p += 1;
  389.     itsEnclosure = theView->itsEnclosure;
  390.     if (itsEnclosure) GetSizeE (itsEnclosure, height_p);
  391. }
  392.  
  393.  
  394. //
  395. //                              DrawHierE
  396. //
  397. // This method draws the hierarchy of enclosures.
  398. //
  399. // Input: theView - the view at the top of this branch of the tree
  400. //        h, v    - the point at which to start drawing (window coordinates)
  401. //        first   - controls whether to draw a stem
  402.  
  403. void CTreeViewer :: DrawHierE (CView *theView, short h, short v, Boolean first)
  404. {
  405.     Str63    name, ID;
  406.     Rect    theRect;
  407.     short    textw;
  408.     long    i;
  409.     CView    *itsEnclosure;
  410.  
  411.     if (!first)
  412.     {
  413.         MoveTo (h, v);
  414.         v -= 4;
  415.         LineTo (h, v);
  416.     }
  417.     SetRect (&theRect, h-kEBoxWidth/2, v-kEBoxHeight, h+kEBoxWidth/2, v);
  418. //
  419. // Draw a gray box if it invisible, with a thick line if it is active, and make
  420. // it a rounded rectangle if the view doesn't want mouse clicks
  421. //
  422.     if (theView->ReallyVisible())
  423.         PenPat (black);
  424.     else
  425.         PenPat (gray);
  426.     if (theView->IsActive())
  427.         PenSize (2,2);
  428.     else
  429.         PenSize (1,1);
  430.     if (theView->GetWantsClicks())
  431.         FrameRect (&theRect);
  432.     else
  433.         FrameRoundRect (&theRect, 12, 12);
  434.     PenPat (black);
  435.     PenSize (1,1);
  436.  
  437.     theView->GetClassName(name);
  438.     i = theView->GetID();
  439.     if ((i < 0 && i >= -32768) || (i > 0 && i <= 32767))
  440.     {
  441.         NumToString (i, ID);
  442.         ConcatPStrings (name, "\p ");
  443.         ConcatPStrings (name, ID);
  444.     }
  445.     if (LongToStr(i, ID))
  446.     {
  447.         ConcatPStrings (name, ID);
  448.     }
  449.     textw = StringWidth (name);
  450.     MoveTo (h - textw/2, v-5);
  451.     DrawString (name);
  452. //
  453. // If this view has an enclosure, call this method recursively to draw it.
  454. //
  455.     itsEnclosure = theView->itsEnclosure;
  456.     if (itsEnclosure) DrawHierE (itsEnclosure, h, v-kEBoxHeight, false);
  457. }
  458.  
  459.  
  460.  
  461. //
  462. //                              ShowSupervisors
  463. //
  464. // This method draws the supervisors of this bureaucrat.  This method is almost
  465. // identical to ShowSubviews: see that method for comments
  466. //
  467.  
  468. void CTreeViewer :: ShowSupervisors (CBureaucrat *theBureaucrat)
  469. {
  470.     short        height, hSize, vSize;
  471.     PicHandle    thePicH;
  472.     Rect        theRect;
  473.     GrafPort    myPort;
  474.     GrafPtr        oldPort, myPortPtr = &myPort;
  475.  
  476.     thePicH = itsPicture->GetMacPicture();
  477.     if (thePicH) KillPicture (thePicH);
  478.  
  479.     height = 0;
  480.     GetSizeS (theBureaucrat, &height);
  481.     hSize = kECellWidth;
  482.     vSize = kECellHeight * height;
  483.  
  484.     SetRect (&theRect, 0, 0, hSize, vSize);
  485.     GetPort (&oldPort);
  486.     OpenPort (myPortPtr);
  487.     SetPort (myPortPtr);
  488.     TextFont (1);
  489.     TextSize (9);
  490.     ClipRect (&theRect);
  491.     thePicH = OpenPicture (&theRect);
  492.     DrawHierS (theBureaucrat, hSize/2, vSize, true);
  493.     ClosePicture ();
  494.     SetPort (oldPort);
  495.     ClosePort (myPortPtr);
  496.  
  497.     InstallPicture (thePicH, hSize, vSize);
  498. }
  499.  
  500.  
  501. //
  502. //                              GetSizeS
  503. //
  504. // This method finds the number of bureaucrats (height) which supervise
  505. // theBureaucrat, up to the gApplication
  506. //
  507.  
  508. void CTreeViewer :: GetSizeS (CBureaucrat *theBureaucrat, short *height_p)
  509. {
  510.     CBureaucrat    *itsSupervisor;
  511.  
  512.     *height_p += 1;
  513.     itsSupervisor = theBureaucrat->itsSupervisor;
  514.     if (itsSupervisor) GetSizeS (itsSupervisor, height_p);
  515. }
  516.  
  517.  
  518. //
  519. //                              DrawHierS
  520. //
  521. // This method draws the hierarchy of supervisors.
  522. //
  523. // Input: theBureaucrat - the view at the top of this branch of the tree
  524. //        h, v          - the point at which to start drawing
  525. //        first         - controls whether to draw a stem
  526.  
  527. void CTreeViewer :: DrawHierS (CBureaucrat *theBureaucrat, short h, short v, Boolean first)
  528. {
  529.     Str63    name, ID;
  530.     Rect    theRect;
  531.     short    textw;
  532.     long    i;
  533.     CBureaucrat    *itsSupervisor;
  534.  
  535.     if (!first)
  536.     {
  537.         MoveTo (h, v);
  538.         v -= 4;
  539.         LineTo (h, v);
  540.     }
  541.     SetRect (&theRect, h-kEBoxWidth/2, v-kEBoxHeight, h+kEBoxWidth/2, v);
  542.     if (member(theBureaucrat, CView))
  543.     {
  544.         if (((CView *)theBureaucrat)->CanBeGopher()) PenSize (2,2);
  545.         FrameRect (&theRect);
  546.     }
  547.     else
  548.     {
  549.         PenSize (2,2);
  550.         FrameRoundRect (&theRect, 12, 12);
  551.     }
  552.     PenSize (1,1);
  553.  
  554.     theBureaucrat->GetClassName(name);
  555.     textw = StringWidth (name);
  556.     MoveTo (h - textw/2, v-5);
  557.     DrawString (name);
  558. //
  559. // If this bureaucrat has a supervisor, call this method recursively to draw it
  560. //
  561.     itsSupervisor = theBureaucrat->itsSupervisor;
  562.     if (itsSupervisor) DrawHierS (itsSupervisor, h, v-kEBoxHeight, false);
  563. }
  564.  
  565.  
  566. //
  567. // This method disables closing the window (since the class can't handle it when
  568. // its window closes), and it enables copying the picture.
  569. //
  570.  
  571. void CTreeViewer :: UpdateMenus()
  572. {
  573.     inherited::UpdateMenus();
  574.     gBartender->DisableCmd(cmdClose);
  575.     gBartender->EnableCmd(cmdCopy);
  576. }
  577.  
  578.  
  579. //
  580. // This method handles copying the picture to the clipboard
  581. //
  582.  
  583. void CTreeViewer :: DoCommand(long theCommand)
  584. {
  585.     switch (theCommand)
  586.     {
  587.         case cmdCopy:
  588.             gClipboard->EmptyScrap();
  589.             gClipboard->PutData('PICT',(Handle)itsPicture->GetMacPicture()); 
  590.             break;
  591.             
  592.         default:
  593.             inherited::DoCommand(theCommand);
  594.             break;
  595.     }
  596. }
  597.  
  598.  
  599. //
  600. // This method installs picture into the scrollpane
  601. //
  602.  
  603. void CTreeViewer :: InstallPicture (PicHandle thePict, short hSize, short vSize)
  604. {
  605.     Rect        sizeRect;
  606.     LongRect    interior;
  607.     short        maxh, maxv;
  608.  
  609.     itsPicture->SetMacPicture (thePict);
  610. //
  611. // The following is just to make sure that zooming out will grow the window only as
  612. // large as the picture.
  613. //
  614.     itsWindow->GetInterior (&interior);
  615.     maxh = MAX(interior.right, hSize+20);
  616.     maxv = MAX(interior.bottom, vSize+20);
  617.     SetRect (&sizeRect, kSVCellDepth + 20, kSVCellWidth + 20, maxh, maxv);
  618.     itsWindow->SetSizeRect (&sizeRect);
  619. //
  620. // Redraw the screen with the new picture
  621. //
  622.     itsScrollPane->Refresh();
  623. }
  624.     
  625.  
  626. //
  627. // This method decides if num is a 4-character string; if so, it returns the
  628. // string enclosed in quotes in theType.
  629. //
  630.  
  631. Boolean CTreeViewer :: LongToStr (long num, Str63 theType)
  632. {
  633.     char c1, c2, c3, c4;
  634.  
  635.     c1 = num >> 24;
  636.     if (c1 < ' ' || c1 > 'z') return false;
  637.     c2 = (num >> 16) & 0xFF;
  638.     if (c2 < ' ' || c2 > 'z') return false;
  639.     c3 = (num >> 8) & 0xFF;
  640.     if (c3 < ' ' || c3 > 'z') return false;
  641.     c4 = num & 0xFF;
  642.     if (c4 < ' ' || c4 > 'z') return false;
  643.     theType[0] = 7;
  644.     theType[1] = ' ';
  645.     theType[2] = '\'';
  646.     theType[3] = c1;
  647.     theType[4] = c2;
  648.     theType[5] = c3;
  649.     theType[6] = c4;
  650.     theType[7] = '\'';
  651.     return true;
  652. }